import { Context } from 'koa';
import { AppContext } from '@/types';
import { File, User } from '@/models';
import * as response from '@/utils/response';
import { validate, commonSchemas } from '@/utils/validator';
import logger from '@/utils/logger';
import Joi from 'joi';
import { Op } from 'sequelize';
import { getFileCategory } from '@/middlewares/upload';
import { uploadFile, downloadFile, deleteFile, deleteFiles } from '@/utils/minio';

/**
 * 文件管理控制器（使用 MinIO）
 */
class FileController {
  /**
   * 上传文件
   */
  async upload(ctx: Context) {
    const appCtx = ctx as AppContext;
    const { user } = appCtx.state;
    // multer 将文件附加到 req 对象，而不是 ctx.request
    const file = (ctx.req as any).file;

    if (!file) {
      return response.error(ctx, '请选择要上传的文件', 400);
    }

    // 检查文件大小（30MB限制）
    const maxSize = 30 * 1024 * 1024; // 30MB
    if (file.size > maxSize) {
      return response.error(ctx, '文件大小不能超过30MB', 400);
    }

    try {
      const category = getFileCategory(file.mimetype);
      
      // 上传到 MinIO
      const uploadResult = await uploadFile(file, file.originalname, {
        'Content-Type': file.mimetype,
      });

      // 保存文件记录到数据库
      const fileRecord = await File.create({
        tenantId: user!.tenantId,
        userId: user!.id,
        originalName: file.originalname,
        fileName: uploadResult.objectName,
        mimeType: file.mimetype,
        size: uploadResult.size,
        path: uploadResult.objectName,
        url: uploadResult.url,
        category,
        storageType: 's3',
      });

      logger.info(`文件上传成功: ${file.originalname} by ${user!.email}`);
      response.success(ctx, fileRecord, '文件上传成功');
    } catch (error) {
      logger.error('文件上传失败:', error);
      ctx.throw(500, '文件上传失败');
    }
  }

  /**
   * 批量上传文件
   */
  async uploadMultiple(ctx: Context) {
    const appCtx = ctx as AppContext;
    const { user } = appCtx.state;
    // multer 将文件附加到 req 对象，而不是 ctx.request
    const files = (ctx.req as any).files;

    if (!files || files.length === 0) {
      return response.error(ctx, '请选择要上传的文件', 400);
    }

    // 检查文件大小（30MB限制）
    const maxSize = 30 * 1024 * 1024; // 30MB
    const oversizedFiles = files.filter((file: any) => file.size > maxSize);
    if (oversizedFiles.length > 0) {
      const fileNames = oversizedFiles.map((f: any) => f.originalname).join(', ');
      return response.error(ctx, `以下文件超过30MB限制: ${fileNames}`, 400);
    }

    try {
      const fileRecords = await Promise.all(
        files.map(async (file: any) => {
          const category = getFileCategory(file.mimetype);
          
          // 上传到 MinIO
          const uploadResult = await uploadFile(file, file.originalname, {
            'Content-Type': file.mimetype,
          });

          return await File.create({
            tenantId: user!.tenantId,
            userId: user!.id,
            originalName: file.originalname,
            fileName: uploadResult.objectName,
            mimeType: file.mimetype,
            size: uploadResult.size,
            path: uploadResult.objectName,
            url: uploadResult.url,
            category,
            storageType: 's3',
          });
        })
      );

      logger.info(`批量上传文件成功: ${files.length} 个文件 by ${user!.email}`);
      response.success(ctx, fileRecords, '批量上传成功');
    } catch (error) {
      logger.error('批量上传文件失败:', error);
      ctx.throw(500, '批量上传失败');
    }
  }

  /**
   * 下载文件
   */
  async download(ctx: Context) {
    const appCtx = ctx as AppContext;
    const { id } = ctx.params;

    try {
      const file = await File.findOne({
        where: {
          id,
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      if (!file) {
        return response.error(ctx, '文件不存在', 404);
      }

      // 从 MinIO 获取文件流
      const fileStream = await downloadFile(file.path);

      // 设置响应头
      ctx.set('Content-Type', file.mimeType);
      ctx.set('Content-Disposition', `attachment; filename="${encodeURIComponent(file.originalName)}"`);

      // 将文件流传输到响应
      ctx.body = fileStream;

      logger.info(`文件下载: ${file.originalName} by ${appCtx.state.user!.email}`);
    } catch (error) {
      logger.error('文件下载失败:', error);
      ctx.throw(500, '文件下载失败');
    }
  }

  /**
   * 获取文件列表
   */
  async list(ctx: Context) {
    const appCtx = ctx as AppContext;
    
    const schema = Joi.object({
      page: commonSchemas.page,
      pageSize: commonSchemas.pageSize,
      keyword: Joi.string().allow('').optional(),
      category: Joi.string().valid('document', 'image', 'video', 'audio', 'other').optional(),
      userId: Joi.string().uuid().optional(),
    });

    const params = await validate<any>(ctx.query as any, schema);
    const { page, pageSize, keyword, category, userId } = params;

    try {
      const where: any = {
        tenantId: appCtx.state.user!.tenantId,
      };

      if (keyword) {
        where.originalName = {
          [Op.like]: `%${keyword}%`,
        };
      }

      if (category) {
        where.category = category;
      }

      if (userId) {
        where.userId = userId;
      }

      const { rows, count } = await File.findAndCountAll({
        where,
        include: [
          {
            model: User,
            as: 'user',
            attributes: ['id', 'email', 'firstName', 'lastName'],
          },
        ],
        order: [['createdAt', 'DESC']],
        limit: pageSize,
        offset: (page - 1) * pageSize,
      });

      response.paginated(ctx, rows, count, page, pageSize);
    } catch (error) {
      logger.error('获取文件列表失败:', error);
      ctx.throw(500, '获取文件列表失败');
    }
  }

  /**
   * 获取单个文件信息
   */
  async getById(ctx: Context) {
    const appCtx = ctx as AppContext;
    const { id } = ctx.params;

    try {
      const file = await File.findOne({
        where: {
          id,
          tenantId: appCtx.state.user!.tenantId,
        },
        include: [
          {
            model: User,
            as: 'user',
            attributes: ['id', 'email', 'firstName', 'lastName'],
          },
        ],
      });

      if (!file) {
        return response.error(ctx, '文件不存在', 404);
      }

      response.success(ctx, file);
    } catch (error) {
      logger.error('获取文件信息失败:', error);
      ctx.throw(500, '获取文件信息失败');
    }
  }

  /**
   * 更新文件信息
   */
  async update(ctx: Context) {
    const appCtx = ctx as AppContext;
    const { id } = ctx.params;

    const schema = Joi.object({
      originalName: Joi.string().max(255).optional(),
      category: Joi.string().valid('document', 'image', 'video', 'audio', 'other').optional(),
    });

    const data = await validate<any>(ctx.request.body as any, schema);

    try {
      const file = await File.findOne({
        where: {
          id,
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      if (!file) {
        return response.error(ctx, '文件不存在', 404);
      }

      await file.update(data);

      logger.info(`文件信息更新: ${file.originalName} by ${appCtx.state.user!.email}`);
      response.success(ctx, file, '更新成功');
    } catch (error) {
      logger.error('更新文件信息失败:', error);
      ctx.throw(500, '更新文件信息失败');
    }
  }

  /**
   * 删除文件
   */
  async delete(ctx: Context) {
    const appCtx = ctx as AppContext;
    const { id } = ctx.params;

    try {
      const file = await File.findOne({
        where: {
          id,
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      if (!file) {
        return response.error(ctx, '文件不存在', 404);
      }

      // 从 MinIO 删除文件
      await deleteFile(file.path);

      // 从数据库删除记录
      await file.destroy();

      logger.info(`文件删除: ${file.originalName} by ${appCtx.state.user!.email}`);
      response.success(ctx, null, '删除成功');
    } catch (error) {
      logger.error('删除文件失败:', error);
      ctx.throw(500, '删除文件失败');
    }
  }

  /**
   * 批量删除文件
   */
  async batchDelete(ctx: Context) {
    const appCtx = ctx as AppContext;

    const schema = Joi.object({
      ids: Joi.array().items(Joi.string().uuid()).min(1).required(),
    });

    const data = await validate<any>(ctx.request.body as any, schema);
    const { ids } = data;

    try {
      const files = await File.findAll({
        where: {
          id: { [Op.in]: ids },
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      if (files.length === 0) {
        return response.error(ctx, '没有找到要删除的文件', 404);
      }

      // 从 MinIO 批量删除文件
      const objectNames = files.map((file) => file.path);
      await deleteFiles(objectNames);

      // 从数据库批量删除记录
      await File.destroy({
        where: {
          id: { [Op.in]: ids },
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      logger.info(`批量删除文件: ${files.length} 个文件 by ${appCtx.state.user!.email}`);
      response.success(ctx, { deletedCount: files.length }, '批量删除成功');
    } catch (error) {
      logger.error('批量删除文件失败:', error);
      ctx.throw(500, '批量删除失败');
    }
  }

  /**
   * 获取文件统计信息
   */
  async getStats(ctx: Context) {
    const appCtx = ctx as AppContext;

    try {
      const totalFiles = await File.count({
        where: {
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      const totalSize = await File.sum('size', {
        where: {
          tenantId: appCtx.state.user!.tenantId,
        },
      });

      const categoryStats = await File.findAll({
        attributes: [
          'category',
          [File.sequelize!.fn('COUNT', '*'), 'count'],
          [File.sequelize!.fn('SUM', File.sequelize!.col('size')), 'size'],
        ],
        where: {
          tenantId: appCtx.state.user!.tenantId,
        },
        group: ['category'],
        raw: true,
      });

      response.success(ctx, {
        totalFiles,
        totalSize: totalSize || 0,
        categoryStats,
      });
    } catch (error) {
      logger.error('获取文件统计信息失败:', error);
      ctx.throw(500, '获取统计信息失败');
    }
  }
}

export default new FileController();
